package designModel;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;

// 代理（Proxy）模式：为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象，从而限制、增强或修改该对象的一些特性。 ☆☆☆☆☆
public class Proxy {
    public static void main(String[] args) {
        // 静态代理
        TargetTeacher targetTeacher = new TargetTeacher();
        ProxyTeacher proxyTeacher = new ProxyTeacher(targetTeacher);
        proxyTeacher.teach();

        // 动态代理
        // 实例化代理类
        ProxyFactory proxyFactory = new ProxyFactory(targetTeacher);// 传入的是目标实现类
        //通过反射构建的类的类型是$开头的，类名不确定，所以可以使用其接口转型
        ITeacher proxy =(ITeacher) proxyFactory.getProxy(); // 得到代理类，此时方法已经被增强了
        proxy.teach();
    }
}

interface ITeacher{
    void teach();
}

// 被代理的目标
class TargetTeacher implements ITeacher{
    @Override
    public void teach() {
        System.out.println("代理教书");
    }
}
// 1 静态代理类
class ProxyTeacher implements ITeacher{
    TargetTeacher teacher;// 聚合进来

    public ProxyTeacher(TargetTeacher teacher) {

        this.teacher = teacher;
    }

    @Override
    public void teach() {
        // 执行代理方法之前 可以多加一些功能
        teacher.teach();// 最终调用的是被代理的对象的teach方法
        // 执行代理方法之后 可以多加一些功能

    }

}

// 2 动态代理
class ProxyFactory { // 代理类工厂
    //创建一个Object类型，因为不知道其具体类型
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }
    // 需要补一下反射的知识
    public Object getProxy() {
        //使用反射创建对象
        /**
         * ClassLoader loader 类加载器，指定动态生成的代理类的类加载器
         * class[] interfaces  获取目标对象实现的全部接口的Class对象的数组
         * InvocationHandler h 设置代理类中和目标类中相同的抽象方法如何重写，需要实现这个接口
         */
        ClassLoader classLoader = this.getClass().getClassLoader();
        System.out.println("classLoader"+classLoader);
        Class<?>[] interfaces = target.getClass().getInterfaces();
        System.out.println("interfaces"+interfaces);
        InvocationHandler h = new InvocationHandler() {
            // 代理中类的方法如何重写 => 保留原功能 只做增强
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object res = null;
                try {
                    // 增强功能
                    System.out.println("Log before executing:\t" + method.getName() + "\t" + Arrays.toString(args));
                    /**
                     * proxy 需要代理的对象   method 表示要执行的方法   args 要执行方法的参数列表 */
                    res = method.invoke(target, args); // 目标对象调用原方法
                    // 增强功能
                    System.out.println("Log after executing:\t" + res);
                } catch (Exception e) {
                    System.out.println("Method Exception");
                } finally {
                    System.out.println("finally");
                }
                return res;
            }
        };
        // 最终返回的是代理类对象
        return java.lang.reflect.Proxy.newProxyInstance(classLoader, interfaces, h);
    }

}